package com.app.protocol;

import com.app.commond.sys.CmdDevAddr;
import com.app.commond.sys.CmdFuncCode;
import com.app.exception.CRC8CheckNotSame;
import com.app.ifs.ICmdKey;

import soft.common.BConvrtUtil;
import soft.common.StringUtil;
import soft.ifs.IByteBuff;
import soft.net.exception.DecodeDataFailException;
import soft.net.model.AReadNetStream;
import soft.net.protocol.IProtocol;

/**
 * 基本数据协议
 * 
 * @author fanpei
 *
 */
public class ProtocolBasic extends AReadNetStream implements IProtocol, ICmdKey {

	public static final byte STARTFLAG = (byte) 0xFE;
	public static final byte ENDFLAG = (byte) 0xFF;

	/**
	 * 偶数地址 header长度
	 */
	public static final short EVENADDR_HEADERLEN = 5;

	/**
	 * 奇数地址 header长度
	 */
	public static final short ODDADDR_HEADERLEN = 4;

	private Direction direc;// 数据方向
	private int lockDevID;// 锁ID
	private byte devAddr = 0;// 地址
	private byte funcCode = 0;// 功能代码
	private byte childfunNum = 0;// 子功能长度
	private int totalLen = -1;// 总数据长度
	private byte[] datas = null;// 数据
	private byte crc = -1;// crc校验码

	public Direction getDirec() {
		return direc;
	}

	public void setDirec(Direction direc) {
		this.direc = direc;
	}

	/**
	 * 锁ID-上行数据
	 * 
	 * @return
	 */
	public int getLockDevID() {
		return lockDevID;
	}

	/**
	 * 获取设备地址
	 * 
	 * @return
	 */
	public byte getDevAddr() {
		return devAddr;
	}

	/**
	 * 获取功能代码
	 * 
	 * @return
	 */
	public byte getFuncCode() {
		return funcCode;
	}

	/**
	 * 子功能个数
	 * 
	 * @return
	 */
	public byte getChildfunNum() {
		return childfunNum;
	}

	/**
	 * 获取数据
	 * 
	 * @return
	 */
	public byte[] getDatas() {
		return datas;
	}

	/**
	 * 数据总长度
	 * 
	 * @return
	 */
	public int getTotalLen() {
		return totalLen;
	}

	/**
	 * 获取CRC校验码
	 * 
	 * @return
	 */
	public byte getCrc() {
		return crc;
	}

	public ProtocolBasic() {
		// Do nothing because of all Args aready are inited
		direc = Direction.UP;
	}

	/**
	 * 上行协议构造函数
	 * 
	 * @param lockDevID
	 *            锁ID
	 * @param devAddr
	 *            设备地址
	 * @param funcCode
	 *            功能代码
	 * @param childfunNum
	 *            子功能数量
	 * @param datas
	 *            数据
	 * @param crc
	 *            crc8校验码
	 */
	public ProtocolBasic(int lockDevID, byte devAddr, byte funcCode, byte childfunNum, byte[] datas, byte crc) {
		this.lockDevID = lockDevID;
		this.devAddr = devAddr;
		this.funcCode = funcCode;
		this.childfunNum = childfunNum;
		this.datas = datas;
		this.crc = crc;
		this.direc = Direction.UP;
	}

	/**
	 * 下行协议构造函数
	 * 
	 * @param devAddr
	 *            设备地址
	 * @param funcCode
	 *            功能代码
	 * @param childfunNum
	 *            子功能数量
	 * @param datas
	 *            数据
	 */
	public ProtocolBasic(byte devAddr, byte funcCode, byte childfunNum, byte[] datas) {
		this.devAddr = devAddr;
		this.funcCode = funcCode;
		this.childfunNum = childfunNum;
		this.datas = datas;
		this.direc = Direction.DOWN;
	}

	@Override
	public void validate() throws CRC8CheckNotSame {
		byte crc1 = CRC8Util.getCRC8((byte) 0, headers);
		byte crc2 = CRC8Util.getCRC8(crc1, datas);
		if (crc != crc2)
			throw new CRC8CheckNotSame(StringUtil.getMsgStr("devAddr:{} funcCode:{}", devAddr, funcCode));
	}

	int devIDLen = 4;
	int devIDHasRead = 0;// 已经读取的设备ID字节
	byte[] lockdevIDs = new byte[4];

	/**
	 * 读取设备地址
	 * 
	 * @param in
	 * @return 是否读取完成
	 */
	private boolean readDevID(IByteBuff in) {
		if (direc == Direction.DOWN)
			return true;

		if (devIDLen != devIDHasRead) {// 设备地址未读取完成
			devIDHasRead = readStreamToArray(in, devIDLen, devIDHasRead, lockdevIDs);
		}
		boolean readfinish = devIDLen == devIDHasRead;
		if (readfinish)
			lockDevID = BConvrtUtil.byteToInt(lockdevIDs);
		return readfinish;
	}

	int headerLen = -1;
	int headerHasRead = 0;// 已经读取的头
	byte[] headers = null;

	@Override
	public void readHeader(IByteBuff in) throws DecodeDataFailException {

		if (STARTFLAG != in.readByte())
			throw new DecodeDataFailException(StringUtil.getMsgStr("data not startwith：{}", STARTFLAG));

		if (readDevID(in)) {
			if (headerLen == -1) {
				byte tmpdevAddr = in.readByte();
				headerHasRead++;
				headerLen = tmpdevAddr % 2 == 0 ? EVENADDR_HEADERLEN : ODDADDR_HEADERLEN;// 如果地址位为偶数的话则为两个字节，奇数则为一个字节
				headers = new byte[headerLen];// 不保存设备ID
				headers[0] = tmpdevAddr;
			}
			headerHasRead = readStreamToArray(in, headerLen, headerHasRead, headers);
			if (headerReadEnough()) {
				parseHeaders();
			}
		}
	}

	@Override
	public void parseHeaders() {
		devAddr = headers[0];
		funcCode = headers[1];
		childfunNum = headers[2];

		byte[] totalLens = null;
		if (headerLen == ODDADDR_HEADERLEN)// 奇数时
		{
			totalLens = new byte[1];
			totalLens[0] = headers[3];
		} else {
			totalLens = new byte[2];
			totalLens[0] = headers[3];
			totalLens[1] = headers[4];
		}
		totalLen = BConvrtUtil.byteToInt(totalLens);
		dataLen = totalLen - headerLen - 1;
		datas = new byte[dataLen];
	}

	@Override
	public boolean headerReadEnough() {
		return headerLen - headerHasRead == 0;
	}

	int dataHasRead = 0;// 已经读取的头
	int dataLen = 0;// 已经读取的头

	@Override
	public void readData(IByteBuff in) {
		dataHasRead = readStreamToArray(in, dataLen, dataHasRead, datas);
	}

	@Override
	public boolean dataReadEnough() {
		return dataLen - dataHasRead == 0;
	}

	boolean enderReadEnough = false;

	@Override
	public void readEnd(IByteBuff in) {
		if (crc < 0)
			crc = in.readByte();
		if (in.readableBytes() > 0 && ENDFLAG == in.readByte()) {
			enderReadEnough = true;
		}

	}

	@Override
	public boolean enderReadEnough() {
		return enderReadEnough;
	}

	@Override
	public String getCmdKey() {
		byte tmpfunCode = funcCode;
		if (devAddr == CmdDevAddr.OX05) {
			tmpfunCode = CmdFuncCode.OX05_HearBeat;
		}
		return CmdKeyUtil.getCmdKey(devAddr, tmpfunCode);
	}

	@Override
	public IProtocol copyProtocol() {
		ProtocolBasic protocol = new ProtocolBasic(lockDevID, devAddr, funcCode, childfunNum, datas, crc);
		protocol.setDirec(direc);
		return protocol;
	}

	@Override
	public void clear() {
		crc = -1;

		devIDHasRead = 0;
		// lockdevIDs = null;

		headerLen = -1;
		headerHasRead = 0;
		headers = null;

		dataHasRead = 0;
		dataLen = 0;
		datas = null;

		enderReadEnough = false;
	}

	@Override
	public byte[] buildSendBytes() {
		dataLen = (byte) (datas == null ? 0 : datas.length);
		headerLen = devAddr % 2 == 0 ? EVENADDR_HEADERLEN : ODDADDR_HEADERLEN;
		totalLen = headerLen + dataLen + 1;
		int sendLen = 1 + totalLen + 1;// 数据总长度:startflag+headers+datas+crc+endflag
		if (direc == Direction.UP)// 上行则加4
			sendLen += 4;

		byte[] sends = new byte[sendLen];
		// 1.开始标志
		sends[0] = STARTFLAG;

		// 2.设备ID
		int lockIDLen = 0;
		if (direc == Direction.UP)// 上行则加4
		{
			lockdevIDs = BConvrtUtil.intToByte(lockDevID);
			sends[1] = lockdevIDs[0];
			sends[2] = lockdevIDs[1];
			sends[3] = lockdevIDs[2];
			sends[4] = lockdevIDs[3];
			lockIDLen = 4;
		}

		// 3.header
		sends[1 + lockIDLen] = devAddr;
		sends[2 + lockIDLen] = funcCode;
		sends[3 + lockIDLen] = childfunNum;

		if (headerLen == EVENADDR_HEADERLEN) {
			byte[] dataLens = BConvrtUtil.shortToByte((short) totalLen);
			sends[4 + lockIDLen] = dataLens[0];
			sends[5 + lockIDLen] = dataLens[1];
		} else
			sends[4 + lockIDLen] = (byte) totalLen;

		// 4.复制数据
		if (datas != null)
			System.arraycopy(datas, 0, sends, 1 + lockIDLen + headerLen, dataLen);

		// 5.crc
		sends[sendLen - 2] = CRC8Util.getCRC8((byte) 0, sends, 1 + lockIDLen, headerLen + dataLen);
		// 6.
		sends[sendLen - 1] = ENDFLAG;

		return sends;
	}

}
